package com.reflect;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.function.Predicate;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.beanutils.BeanUtils;

import com.util.ArrayUtil;
import com.util.DateUtil;
import com.util.EnumUtil;

/**javabean和map互换
 * @author dell
 *
 */
public class MapBeanExchangeUtil {

	/**将一个 Map对象转化为一个 JavaBean，该功能不完善，还要继续加对不同类型的处理，如果有对象嵌套对象，要递归调用
	 * @param type 要转化的类型
	 * @param map 包含属性值的map
	 * @return
	 * @throws IntrospectionException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws InvocationTargetException
	 */
	public static <T> T convertToBean(Class<T> type, Map<String, String> map) throws IntrospectionException, IllegalAccessException, InstantiationException, InvocationTargetException {
        BeanInfo beanInfo = Introspector.getBeanInfo(type);
        T obj = type.newInstance();

        PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();
        Arrays.stream(propertyDescriptors).forEach(descriptor -> {
        	String propertyName = descriptor.getName();
            if (map.containsKey(propertyName)) {
            	try{
            		String value = map.get(propertyName);//全部都是从request里获取的string类型
					if("".equals(value))
						return;
            		Object value2 = null;//进行类型转换后的真实值
            		if(descriptor.getPropertyType().equals(Date.class)){//对日期类型处理
            			value2 = DateUtil.getDateByString(value);
            		}else if(descriptor.getPropertyType().equals(Integer.class)){//对Integer类型处理
            			value2 = Integer.parseInt(value);
            		}else if(descriptor.getPropertyType().equals(Long.class)){//对Long类型处理
            			value2 = Long.parseLong(value);
            		}else if(descriptor.getPropertyType().isEnum()){//处理enum
            			value2 = EnumUtil.reEnumByValue(descriptor.getPropertyType().getEnumConstants(), value);
            		}else if(descriptor.getPropertyType().equals(Byte.class)){//对Byte类型处理
            			value2 = Byte.parseByte(value);
            		}else if(descriptor.getPropertyType().equals(BigDecimal.class)){//对BigDecimal类型处理
            			value2 = new BigDecimal(value);
            		}else if(descriptor.getPropertyType().equals(Byte[].class)){
            			value2 = ArrayUtil.strToByteArray(value);
            		}else{
            			value2 = value;//String类型不用处理
            		}

	                Object[] args = new Object[1];
	                args[0] = value2;

	                descriptor.getWriteMethod().invoke(obj, args);
            	}catch(Exception e){
            		e.printStackTrace();
            	}
            }
        });
        return obj;
    }

	/**将一个 JavaBean对象转化为一个 Map，该功能不完善，还要继续加对不同类型的处理，如果有对象嵌套对象，要递归调用
	 * @param map 要转化的map
	 * @param type 转换后的bean
	 * @return
	 */
	public static <T> Map<String, Object> convertToMap(Map<String, Object> map, T obj) {
		try{
			BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
			PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();
			Arrays.stream(propertyDescriptors).forEach(descriptor -> {
				String propertyName = descriptor.getName();
				try{
					Object value2 = descriptor.getReadMethod().invoke(obj);//进行类型转换后的真实值
					map.put(propertyName, value2);
				}catch(Exception e){
					e.printStackTrace();
				}
			});
		}catch(Exception e){
			e.printStackTrace();
		}
		return map;
	}

	/**对象拷贝
	 * @param sourceBean 源对象
	 * @param newBean 新对象
	 */
	public static <T> T copyBean(T source, T dest){
		try {
			BeanUtils.copyProperties(dest, source);
			return dest;
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			return null;
		} catch (InvocationTargetException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**对象拷贝，只拷贝指定属性
	 * @param source
	 * @param dest
	 * @param propNames 指定属性
	 * @return
	 * @throws Exception
	 */
	public static <T, K> K definePropCopyBean(T source, K dest, String... propNames) {
		if(source == null || dest == null){
			return null;
		}
		try{
			//获取属性
			BeanInfo sourceBean = Introspector.getBeanInfo(source.getClass(), java.lang.Object.class);
			PropertyDescriptor[] sourceProperty = sourceBean.getPropertyDescriptors();

			BeanInfo destBean = Introspector.getBeanInfo(dest.getClass(), java.lang.Object.class);
			PropertyDescriptor[] destProperty = destBean.getPropertyDescriptors();

			for(int i=0;i<sourceProperty.length;i++){
				PropertyDescriptor sourceProp = sourceProperty[i];
				if(Arrays.stream(propNames).filter(name -> sourceProp.getName().equals(name)).findFirst().isPresent()){
					for(int j=0;j<destProperty.length;j++){
						if(sourceProp.getName().equals(destProperty[j].getName())){
							if(destProperty[j].getWriteMethod() != null && sourceProp.getReadMethod() != null){
								//调用source的getter方法和dest的setter方法
								destProperty[j].getWriteMethod().invoke(dest, sourceProp.getReadMethod().invoke(source));
								break;
							}
						}
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();
			return dest;
		}

		return dest;
	}

	/**对象拷贝，只拷贝非空属性
	 * @param source
	 * @param dest
	 * @param isAnno 是否按属性上的注解复制
	 * @return
	 * @throws Exception
	 */
	public static <T, K> K notNullCopyBean(T source, K dest, boolean isAnno) {
		if(source == null || dest == null){
			return null;
		}
		try{
	        //获取属性
	        BeanInfo sourceBean = Introspector.getBeanInfo(source.getClass(), java.lang.Object.class);
	        PropertyDescriptor[] sourceProperty = sourceBean.getPropertyDescriptors();

	        BeanInfo destBean = Introspector.getBeanInfo(dest.getClass(), java.lang.Object.class);
	        PropertyDescriptor[] destProperty = destBean.getPropertyDescriptors();

            for(int i=0;i<sourceProperty.length;i++){
            	String sourcePropertyName = sourceProperty[i].getName();
            	if(isAnno){
            		Field f = null;
            		try{
            			f = getDeclaredField(source.getClass(), sourceProperty[i].getName());
            		}catch(Exception e){
            			continue;
            		}
            		if(f != null){
	            		Annotation[] annos = f.getAnnotations();
	            		for(int z=0; z<annos.length; z++){
	            			Annotation anno = annos[z];
	            			if(anno instanceof CopyAlias){
	            				sourcePropertyName = ((CopyAlias)anno).value();
	            			}
	            		}
            		}
            	}
                for(int j=0;j<destProperty.length;j++){
                    if(sourcePropertyName.equals(destProperty[j].getName())){
                    	if(destProperty[j].getWriteMethod() != null && sourceProperty[i].getReadMethod() != null && sourceProperty[i].getReadMethod().invoke(source) != null){
	                        //调用source的getter方法和dest的setter方法
	                        destProperty[j].getWriteMethod().invoke(dest, sourceProperty[i].getReadMethod().invoke(source));
	                        break;
                    	}
                    }
                }
            }
        }catch(Exception e){
        	e.printStackTrace();
        	return dest;
        }

        return dest;
    }

	/**对象拷贝，只拷贝非空属性
	 * @param source
	 * @param dest
	 * @return
	 * @throws Exception
	 */
	public static <T, K> K notNullCopyBean(T source, K dest) {
		return notNullCopyBean(source, dest, false);
    }

	/**对象拷贝，只拷贝非空属性，源对象的所有属性都是可以转换为String类型的，会根据目标对象的属性进行类型转换
	 * @param source
	 * @param dest
	 * @return
	 * @throws Exception
	 */
	public static <T, K> K notNullCopyStringBean(T source, K dest) {
		if(source == null || dest == null){
			return null;
		}
		try{
	        //获取属性
	        BeanInfo sourceBean = Introspector.getBeanInfo(source.getClass(), java.lang.Object.class);
	        PropertyDescriptor[] sourceProperty = sourceBean.getPropertyDescriptors();

	        BeanInfo destBean = Introspector.getBeanInfo(dest.getClass(), java.lang.Object.class);
	        PropertyDescriptor[] destProperty = destBean.getPropertyDescriptors();

            for(int i=0;i<sourceProperty.length;i++){
                for(int j=0;j<destProperty.length;j++){
                    if(sourceProperty[i].getName().equals(destProperty[j].getName())){
                    	if(destProperty[j].getWriteMethod() != null && sourceProperty[i].getReadMethod() != null && sourceProperty[i].getReadMethod().invoke(source) != null){
	                        //调用source的getter方法和dest的setter方法
                    		if(destProperty[j].getPropertyType().equals(BigDecimal.class)){
		                        destProperty[j].getWriteMethod().invoke(dest, new BigDecimal(sourceProperty[i].getReadMethod().invoke(source).toString()));
                    		}
                    		if(destProperty[j].getPropertyType().equals(Integer.class)){
                    			destProperty[j].getWriteMethod().invoke(dest, Integer.parseInt(sourceProperty[i].getReadMethod().invoke(source).toString()));
                    		}
                    		if(destProperty[j].getPropertyType().equals(Byte.class)){
                    			destProperty[j].getWriteMethod().invoke(dest, Byte.parseByte(sourceProperty[i].getReadMethod().invoke(source).toString()));
                    		}
                    		if(destProperty[j].getPropertyType().equals(Date.class)){
                    			destProperty[j].getWriteMethod().invoke(dest, DateUtil.getDateByString(sourceProperty[i].getReadMethod().invoke(source).toString()));
                    		}
                    		if(destProperty[j].getPropertyType().equals(String.class)){
                    			destProperty[j].getWriteMethod().invoke(dest, sourceProperty[i].getReadMethod().invoke(source).toString());
                    		}
                    		if(destProperty[j].getPropertyType().isEnum()){
                    			destProperty[j].getWriteMethod().invoke(dest, EnumUtil.reEnumByValue(destProperty[j].getPropertyType().getEnumConstants(), sourceProperty[i].getReadMethod().invoke(source).toString()));
                    		}
                    		break;
                    	}
                    }
                }
            }
        }catch(Exception e){
        	e.printStackTrace();
        	return dest;
        }

        return dest;
    }

	/**把request非空属性拷贝到目标对象dest
	 * @param source
	 * @param dest
	 * @return
	 */
	public static <K> K notNullCopyBean(HttpServletRequest source, K dest) {
		if(source == null || dest == null){
			return null;
		}
		try{
	        //获取属性
	        BeanInfo destBean = Introspector.getBeanInfo(dest.getClass(), java.lang.Object.class);
	        PropertyDescriptor[] destProperty = destBean.getPropertyDescriptors();

            for(int i=0;i<destProperty.length;i++){
                if(source.getParameter(destProperty[i].getName()) != null){
                    //调用dest的setter方法
                    destProperty[i].getWriteMethod().invoke(dest, source.getParameter(destProperty[i].getName()));
                }
            }
        }catch(Exception e){
        	e.printStackTrace();
        	return dest;
        }

        return dest;
    }

	/**判断指定属性是否满足条件，可以传多个属性，判断是否全满足条件
	 * @param source
	 * @param predicate 判断条件
	 * @param propNames
	 * @return
	 * @throws Exception
	 */
	public static <T> boolean notNullProp(T source, Predicate<Object> predicate, String... propNames) {
		boolean result = true;
		try{
			//获取属性
	        BeanInfo sourceBean = Introspector.getBeanInfo(source.getClass(), java.lang.Object.class);
	        PropertyDescriptor[] sourceProperty = sourceBean.getPropertyDescriptors();

	        for(int i=0; i<propNames.length; i++){
				for(int j=0; j<sourceProperty.length; j++){
                    if(propNames[i].equals(sourceProperty[j].getName())){
                    	if(sourceProperty[j].getReadMethod() != null && !predicate.test(sourceProperty[j].getReadMethod().invoke(source))){
	                        return false;
                    	}
                    }
                }
			};
        }catch(Exception e){
        	e.printStackTrace();
        }

        return result;
    }

	/**
     * 循环向上转型, 获取对象的 DeclaredField
     * @param object : 子类对象
     * @param fieldName : 父类中的属性名
     * @return 父类中的属性对象
     */
	private static Field getDeclaredField(Class<?> clazz, String fieldName){
        Field field = null;
        for(; clazz != Object.class ; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField(fieldName);
                return field;
            } catch (Exception e) {
            }
        }

        return null;
    }

}
